"""
微信防撤回模块
"""
import datetime
import os
import pickle
from functools import wraps

import itchat
from lxml import etree

from commons.db import Cache, MongoClient
from commons.log import LogFactory
from filter.blocked_room_filter import BlockedRoomFilter
from models.message_model import MessageModel
from service.contact_service import ContactService
from wbot.settings import ATTACHMENT_DIR

logger = LogFactory("message_service").get_logger()


class AttachProxy:

    def __init__(self, msg):
        self.fileName = msg.fileName
        self.MsgId = msg.MsgId
        self.CreateTime = msg.CreateTime
        self.Content = msg.Content
        self.Type = msg.Type
        self.fileDir = os.path.join(ATTACHMENT_DIR, msg.fileName)
        self.User = msg.User


def msg_time(func):
    """消息对象增加发送时间"""

    @wraps(func)
    def wrapper(msg, *args, **kwargs):
        create_time = datetime.datetime.now()
        msg.CreateTime = create_time
        res = func(msg, *args, **kwargs)
        return res

    return wrapper


@msg_time
def text_msg(msg, is_group):
    """
    文字消息的保存

    :param msg:
    :return:
    """
    contact_service = ContactService()
    blocked_filter = BlockedRoomFilter()
    c = Cache()
    obj = pickle.dumps(msg)
    c.set(msg.MsgId, obj)

    if is_group:
        if blocked_filter.filter(msg):
            return
        gname = None
        if hasattr(msg.User, 'NickName'):
            gname = msg.User.NickName  # 若未扫描到群组，会取不到这个属性
        name = f'[{gname if gname else "未命名群组"}]{msg.ActualNickName}'
    else:
        name = contact_service.find_name(msg.FromUserName)
    msg_model = MessageModel(name, msg.Text)
    MongoClient.save_message(msg_model.jsonfy())
    logger.info("%s: %s", name, msg.Text)


@msg_time
def multi_msg(msg):
    """
    图片语音消息的保存

    :param msg:
    :return:
    """

    msg.download(os.path.join(ATTACHMENT_DIR, msg.fileName))
    msg.fileDir = os.path.join(ATTACHMENT_DIR, msg.fileName)

    # 带有附件的msg.Text序列化会有问题，使用可序列化类代替
    msg_proxy = AttachProxy(msg)

    # 序列化到redis
    c = Cache()
    obj = pickle.dumps(msg_proxy)
    c.set(msg.MsgId, obj)


@msg_time
def map_msg(msg):
    """
    定位地点的保存

    :param msg:
    :return:
    """
    url = msg.Url
    location = msg.Text
    msg.reply = '\n'.join([location, url])

    c = Cache()
    obj = pickle.dumps(msg)
    c.set(msg.MsgId, obj)


@msg_time
def sharing_msg(msg):
    """
    分享地址的保存

    :param msg:
    :return:
    """
    et = etree.fromstring(msg.Content)
    title = et.xpath('/msg/appmsg/title')[0].text
    url = et.xpath('/msg/appmsg/url')[0].text
    msg.reply = '\n'.join((title, url))

    c = Cache()
    obj = pickle.dumps(msg)
    c.set(msg.MsgId, obj)


@msg_time
def card_msg(msg):
    """
    名片头像地址，昵称

    :param msg:
    :return:
    """
    et = etree.fromstring(msg.Content)
    root = et.xpath('/msg')[0]
    header_img = root.get('bigheadimgurl')
    nickname = root.get('nickname')
    msg.reply = '\n'.join([nickname, header_img])

    c = Cache()
    obj = pickle.dumps(msg)
    c.set(msg.MsgId, obj)


def note_msg(msg, isGroup, isMp):
    """
    红包、撤回消息类型处理

    :param msg:
    :return:
    """
    if '红包' in msg.Content:
        # 红包消息内容：收到红包，请在手机上查看
        if not isGroup:
            logger.info("收到%s的红包", msg.User.NickName)
    elif '群聊' in msg.Content:
        gname = msg.User.NickName
        name = gname if gname else "未命名群组"
        logger.info("[%s]: %s", name, msg.Text)
    else:
        root = etree.fromstring(msg.Content)
        if root.tag == 'sysmsg' and root[0].tag == 'revokemsg':
            # 回撤的MsgId
            msg_id = root.xpath('/sysmsg/revokemsg/msgid')[0].text
            # 系统提示的文字
            replacemsg = root.xpath('/sysmsg/revokemsg/replacemsg')[0].text

            c = Cache()
            res = c.get(msg_id)

            if res:
                revoke_msg = pickle.loads(res)  # 被撤回的消息
                # 只能以isGroup区分是否群消息
                group_name = f"[{revoke_msg.User.NickName}]" if isGroup else ''

                if revoke_msg.Type == 'Text':
                    # 文本消息撤回
                    logger.info("%s%s撤回消息：%s", group_name, replacemsg[:-7], revoke_msg.Text)
                    itchat.send_msg(
                        f"{group_name}{replacemsg[:-5]}:\n{revoke_msg.Text}\n"
                        f"{revoke_msg.CreateTime:%Y-%m-%d(%a) %H:%M:%S}",
                        toUserName='filehelper')
                elif revoke_msg.Type in ('Picture', 'Recording', 'Video', 'Attachment'):
                    # 文件撤回，先发送回撤消息，再发送回撤内容
                    logger.info("撤回文件：%s", revoke_msg.fileDir)
                    itchat.send_msg(
                        f"{group_name}{replacemsg[:-5]}:\n{revoke_msg.Type}\n"
                        f"{revoke_msg.CreateTime:%Y-%m-%d(%a) %H:%M:%S}",
                        toUserName='filehelper')

                    type_symbol = {
                        'Picture': 'img',
                        'Video': 'vid',
                    }.get(revoke_msg.Type, 'fil')
                    itchat.send(f'@{type_symbol}@{revoke_msg.fileDir}', toUserName='filehelper')
                elif revoke_msg.Type in ('Map', 'Card', 'Sharing'):
                    # 地点、名片回撤
                    logger.info("撤回%s", revoke_msg.Type)
                    itchat.send(
                        f"{group_name}{replacemsg[:-5]}:\n{revoke_msg.reply}\n"
                        f"{revoke_msg.CreateTime:%Y-%m-%d(%a) %H:%M:%S}",
                        toUserName='filehelper')

            else:
                logger.warning("不能撤回的消息")

        elif root.tag == 'msg':
            # 转账
            logger.info("收到转账消息")
